package com.nulldev.util.io.nStreams;

import java.io.IOException;

import com.nulldev.util.math.lNumber;
import com.nulldev.util.other.ByteUtils;

public class ByteArrayStream implements nStream {
	
	private boolean readable, writeable;
	private volatile byte[] data;
	private volatile lNumber position = lNumber.nil();
	
	public ByteArrayStream(final byte[] data) {
		this.data = data;
		this.readable = true;
	}
	
	public ByteArrayStream(final int allocated) {
		this.data = new byte[0];
		this.writeable = true;
	}
	
	public ByteArrayStream(final byte[] data, final boolean writeable) {
		this.data = data;
		this.readable = true;
		this.writeable = writeable;
	}

	@Override
	@Deprecated
	public void sync() {
	}

	@Override
	public void close() throws IOException {
		this.data = null;
		System.gc();
	}
	
	@Override
	public void reset() throws UnsupportedOperationException {
		this.position.setSelf(0L);
	}

	@Override
	public int positionLegacy() {
		return this.position.intValue();
	}

	@Override
	public lNumber position() {
		return this.position;
	}

	@Override
	public byte[] next(final int bytes) throws IOException {
		if (!this.readable()) {
			throw new IOException("Cannot read array!");
		}else if (this.data == null || this.data.length == 0) {
			throw new IOException("Stream already closed!");
		}else if (this.position.add(bytes).moreThan(this.data.length)) {
			throw new IOException("You have read the full array!");
		}
		byte[] alloc = new byte[bytes];
		for (int i = 0; i < bytes; i++) {
			alloc[i] = (byte) next();
		}
		return alloc;
	}

	@Override
	public int next() throws IOException {
		if (!this.readable()) {
			throw new IOException("Cannot read array!");
		}else if (this.data == null || this.data.length == 0) {
			throw new IOException("Stream already closed!");
		}else if (this.position.add(1).moreThan(this.data.length)) {
			throw new IOException("You have read the full array!");
		}
		this.position.addSelf(1);
		return this.data[this.position.intValue()];
	}

	@Override
	public int at(int position) throws IOException {
		if (!this.readable()) {
			throw new IOException("Cannot read array!");
		}else if (this.data == null) {
			throw new IOException("Stream already closed!");
		}
		return this.data[position];
	}

	@Override
	public boolean readable() {
		return this.readable;
	}

	@Override
	public boolean writeable() {
		return this.writeable;
	}

	@Override
	public void write(int _byte) throws IOException {
		if (!this.writeable()) {
			throw new IOException("Cannot write data into array!");
		}else if (this.data == null) {
			throw new IOException("Stream already closed!");
		}else if (data == null || data.length == 0) {
			throw new IOException("Cannot write nothing or null!");
		}
		this.data = ByteUtils.add(this.data, (byte) _byte);
	}

	@Override
	public void write(byte[] data) throws IOException {
		if (!this.writeable()) {
			throw new IOException("Cannot write data into array!");
		}else if (this.data == null) {
			throw new IOException("Stream already closed!");
		}else if (data == null || data.length == 0) {
			throw new IOException("Cannot write nothing or null!");
		}
		this.data = ByteUtils.concatenateByteArrays(this.data, data);
	}

}
